package com.monochromebytes.mononet.virtual;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import com.monochromebytes.mononet.packet.Outbox;
import com.monochromebytes.mononet.packet.OutboxListener;
import com.monochromebytes.mononet.packet.OutgoingPacket;

/**
 * A simple implementation of an Outbox.
 * 
 * This implementation of an Outbox will accept every added packet and sends
 * them out in that order they were added.
 * 
 * @author ted
 */
public class QueuedOutbox implements Outbox {

    /**
     * The packets that will be sent out.
     */
    private final LinkedList<OutgoingDataPacket> packets;

    /**
     * The listeners that will be informed about new added packets to send out.
     */
    private final ArrayList<OutboxListener>      listeners;

    /**
     * Creates a new empty SimpleOutbox.
     */
    public QueuedOutbox() {
        this.packets = new LinkedList<OutgoingDataPacket>();
        this.listeners = new ArrayList<OutboxListener>();
    }

    /**
     * Returns the next packet that is to send.
     * 
     * The packet will be removed from this Outbox.
     * 
     * If there is not packet to sent, this method will return null.
     */
    @Override
    public OutgoingPacket getNextPacket() {
        synchronized (this.packets) {
            if (this.packets.isEmpty()) {
                return null;
            }
            OutgoingDataPacket packet = this.packets.removeFirst();
            return packet.getUDPPacket();
        }
    }

    /**
     * Adds the packet to this Outbox to send it out.
     * 
     * It's up to its implementation when the packet will be sent.
     * 
     * @param packet
     *            The packet that will be added to the Outbox.
     */
    public void sendPacket(OutgoingDataPacket packet) {
        synchronized (this.packets) {
            this.packets.add(packet);
        }
        synchronized (this.listeners) {
            for (OutboxListener listener : this.listeners) {
                listener.outgoingPackedAdded();
            }
        }
    }

    /**
     * Adds the given packets to this Outbox to send them out.
     * 
     * It's up to its implementation when and in which order the packets will be
     * sent.
     * 
     * @param packets
     *            The packets that will be added to the Outbox.
     */
    public void sendPackets(List<OutgoingDataPacket> packets) {
        synchronized (this.packets) {
            this.packets.addAll(packets);
        }
        synchronized (this.listeners) {
            for (OutboxListener listener : this.listeners) {
                listener.outgoingPackedAdded();
            }
        }
    }

    /**
     * Returns the number of packets this Outbox contains.
     * 
     * @return The number of packets this Outbox contains.
     */
    public int size() {
        synchronized (this.packets) {
            return this.packets.size();
        }
    }

    @Override
    public void addOutboxListener(OutboxListener listener) {
        synchronized (this.listeners) {
            this.listeners.add(listener);
        }
    }

    @Override
    public void removeOutboxListener(OutboxListener listener) {
        synchronized (this.listeners) {
            this.listeners.add(listener);
        }
    }
}